package no.rmz.genetics;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public final class ClassStructureRepository {

    /**
     * All the field values of a class.
     */
    private static Map<Class, Map<String, FieldValue>> fieldValueMap =
        new HashMap<>();

    private  synchronized static Map<String, FieldValue>getClassFieldValueMap(final Class c) {
        if (!fieldValueMap.containsKey(c)) {
            fieldValueMap.put(c, new TreeMap<String, FieldValue>());
            parseClass(c); // XXX Whoooo!
        }
        return fieldValueMap.get(c);
    }



    public synchronized static  void reset() {
        fieldValueMap = null;
    }

    public static FieldValue getFieldValue(final Class c, final String fieldname) {
        final Map<String, FieldValue> fieldMap = getClassFieldValueMap(c);
        synchronized(fieldMap) {
        	final FieldValue result;
        	if (fieldMap.containsKey(fieldname)) {
        		result = fieldMap.get(fieldname);
        	} else {
        		result = new FieldValue(fieldname);
        		fieldMap.put(fieldname, result);
        	}
        	return result;
        }
    }

    public static  Collection<FieldValue> getFieldValues(final Class c) {
        return getClassFieldValueMap(c).values();
    }


    /**
     * Extract set/get fields pairs that are genetic.
     * XXX A bit too long method.
     */
    public static void parseClass(final Class c) {
        for (final Method m: c.getMethods()) {
            if (InstanceCombinator.isGenomeAccessor(m)) {
                final String     fieldname = InstanceCombinator.getFieldName(m);
                final FieldValue        fv = getFieldValue(c, fieldname);
                final Class<?>[]    ptypes = m.getParameterTypes();
                final Class<?>        rtype= m.getReturnType();
                final Class<?>         fvt = fv.getValueType();



                System.out.println("fvt pre = " + fv);


                if (InstanceCombinator.isGetMethod(m)) {

                    if (ptypes != null && (ptypes.length > 0)) {
                        throw new RuntimeException("Genetic get can't have parameters");
                    }

                    final Class retType =  m.getReturnType();

                    if (fvt != null) {
                        if (retType != fvt) {
                            throw new RuntimeException("Return type doesnt match field value type"); // XXX
                        } 
                    } else {
                        fv.setValueType(retType);
                    }
                    // XX Should assert that getter not already set
                    fv.setGetter(m);

                } else if (InstanceCombinator.isSetMethod(m)) {
                    if (ptypes.length != 1) {
                        throw new RuntimeException("Genetic Set must have one parameter");
                    }
                    final Class paramType = ptypes[0];
                    if (fvt != null) {
                        if (paramType != fvt) {
                            throw new RuntimeException("Type of genetic set does not match corresponding get");
                        }
                    }  else {
                        fv.setValueType(paramType);
                    }
                    // XXX Should assert that setter not already set
                    fv.setSetter(m);

                }
                System.out.println("fvt post= " + fv);
            }
        }
    }
}
